package com.fpx.open.platform.common;

import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;

import org.apache.log4j.NDC;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.validation.BindException;
import org.springframework.web.HttpMediaTypeNotSupportedException;
import org.springframework.web.bind.ServletRequestBindingException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.support.RequestContext;

import com.fpx.open.platform.common.constant.AppConstant;
import com.fpx.open.platform.common.constant.BaseConstant;
import com.fpx.open.platform.common.constant.ErrorCodeConstant;
import com.fpx.open.platform.common.util.JsonBinder;
import com.fpx.open.platform.domain.exception.ServiceException;
import com.fpx.open.platform.domain.vo.ResultVo;
import com.fpx.open.platform.service.MessageService;

/**
 * 异常处理类
 * @author sunkey
 * @date Mar 15, 2013 6:20:19 PM
 * @version 1.0.0 
 * @copyright fpx.com 
 */
public class AppExceptionHandler {

    private final Logger logger = LoggerFactory.getLogger(getClass());
	
	public final static String FIELD = "field";
	public final static String ERROR_INFO = "errorInfo";
	public final static String ERROR_CODE = "errorCode";
	public final static String DATA = "data";

	@Resource
	private MessageService messageService;
	
	/**
	 * 数据格式请求不符合json格式异常返回的错误信息
	 * @param HttpMessageNotReadableException
	 * @param request
	 * @return resultVo
	 */
	@ExceptionHandler(HttpMessageNotReadableException.class)
	@ResponseBody
	@SuppressWarnings({ "rawtypes"})
	public ResultVo handleServiceException(HttpMessageNotReadableException ex, HttpServletRequest request) {
		ResultVo resultVo = new ResultVo();
		resultVo.setErrorMsg(messageService.getMessage(ErrorCodeConstant.E_0006, getLocale(request)));
		
		resultVo.setErrorCode(ErrorCodeConstant.E_0006);
		
		NDC.push(ERROR_CODE+BaseConstant.SYMBOL_COLON+ErrorCodeConstant.E_0006);
		NDC.push(ERROR_INFO+BaseConstant.SYMBOL_COLON+resultVo.getErrorMsg());
		logger.error(ex.getMessage(),ex);
		
		request.setAttribute(AppConstant.RESP_CONTENT, resultVo);
		
		return resultVo;
	}

	/**
	 * 数据库异常返回的错误信息
	 * @param HttpMessageNotReadableException
	 * @param request
	 * @return resultVo
	 */
	@ExceptionHandler(SQLException.class)
	@ResponseBody
	@SuppressWarnings({ "rawtypes"})
	public ResultVo handleSQLException(SQLException ex, HttpServletRequest request) {
		ResultVo resultVo = new ResultVo();
		resultVo.setErrorMsg(messageService.getMessage(ErrorCodeConstant.E_0104, getLocale(request)));
		
		resultVo.setErrorCode(ErrorCodeConstant.E_0104);
		
		NDC.push(ERROR_CODE+BaseConstant.SYMBOL_COLON+ErrorCodeConstant.E_0104);
		NDC.push(ERROR_INFO+BaseConstant.SYMBOL_COLON+resultVo.getErrorMsg());
		logger.error(ex.getMessage(),ex);
		
		request.setAttribute(AppConstant.RESP_CONTENT, resultVo);
		
		return resultVo;
	}

	/**
	 * 数据库异常返回的错误信息
	 * @param HttpMessageNotReadableException
	 * @param request
	 * @return resultVo
	 */
	@ExceptionHandler(DuplicateKeyException.class)
	@ResponseBody
	@SuppressWarnings({ "rawtypes"})
	public ResultVo handleDuplicateKeyException(DuplicateKeyException ex, HttpServletRequest request) {
		ResultVo resultVo = new ResultVo();
		resultVo.setErrorMsg(messageService.getMessage(ErrorCodeConstant.E_0008, getLocale(request)));
		
		resultVo.setErrorCode(ErrorCodeConstant.E_0008);
		
		NDC.push(ERROR_CODE+BaseConstant.SYMBOL_COLON+ErrorCodeConstant.E_0008);
		NDC.push(ERROR_INFO+BaseConstant.SYMBOL_COLON+resultVo.getErrorMsg());
		logger.error(ex.getMessage(),ex);
		
		request.setAttribute(AppConstant.RESP_CONTENT, resultVo);
		
		return resultVo;
	}
	
	/**
	 * content type不支持
	 * @param ex
	 * @param request
	 * @return
	 */
	@ExceptionHandler(HttpMediaTypeNotSupportedException.class)
	@ResponseBody
	@SuppressWarnings({ "rawtypes"})
	public ResultVo handleHttpMediaTypeException(HttpMediaTypeNotSupportedException ex, HttpServletRequest request) {
		ResultVo resultVo = new ResultVo();
		resultVo.setErrorMsg(messageService.getMessage(ErrorCodeConstant.E_0005, getLocale(request)));
		
		resultVo.setErrorCode(ErrorCodeConstant.E_0005);
		
		NDC.push(ERROR_CODE+BaseConstant.SYMBOL_COLON+ErrorCodeConstant.E_0005);
		NDC.push(ERROR_INFO+BaseConstant.SYMBOL_COLON+resultVo.getErrorMsg());
		logger.error(ex.getMessage(),ex);
		
		request.setAttribute(AppConstant.RESP_CONTENT, resultVo);
		
		return resultVo;
	}
	
	/**
	 * 数据绑定错误
	 * @param ex
	 * @param request
	 * @return
	 */
	@ExceptionHandler({ServletRequestBindingException.class,BindException.class})
	@ResponseBody
	@SuppressWarnings({ "rawtypes"})
	public ResultVo handleServletRequestBindingException(Exception ex, HttpServletRequest request) {
		ResultVo resultVo = new ResultVo();
		resultVo.setErrorMsg(messageService.getMessage(ErrorCodeConstant.E_0007, getLocale(request)));
		
		resultVo.setErrorCode(ErrorCodeConstant.E_0007);
		
		NDC.push(ERROR_CODE+BaseConstant.SYMBOL_COLON+ErrorCodeConstant.E_0007);
		NDC.push(ERROR_INFO+BaseConstant.SYMBOL_COLON+resultVo.getErrorMsg());
		logger.error(ex.getMessage(),ex);
		
		request.setAttribute(AppConstant.RESP_CONTENT, resultVo);
		
		return resultVo;
	}
	
	/**
	 * 业务逻辑处理异常ServiceException返回的错误信息
	 * @param ServiceException
	 * @param request
	 * @return ResultVo
	 */
	@ExceptionHandler(ServiceException.class)
	@ResponseBody
	@SuppressWarnings({ "rawtypes"})
	public ResultVo handleServiceException(ServiceException ex, HttpServletRequest request) {
		ResultVo resultVo = new ResultVo();
		resultVo.setErrorMsg(messageService.getMessage(ex.getErrorCode(), ex.getObject(), getLocale(request)));
		resultVo.setErrorCode(ex.getErrorCode());

		NDC.push(ERROR_CODE+BaseConstant.SYMBOL_COLON+ex.getErrorCode());
		NDC.push(ERROR_INFO+BaseConstant.SYMBOL_COLON+resultVo.getErrorMsg());
		if(resultVo.getData() != null){
			NDC.push(DATA+BaseConstant.SYMBOL_COLON+JsonBinder.buildNonDefaultBinder().toJson(resultVo.getData()));
		}
		logger.error(ex.getMessage(), ex);
		
		request.setAttribute(AppConstant.RESP_CONTENT, resultVo);
		
		return resultVo;
	}

	/**
	 * 内部错误超类异常Exception返回的错误信息
	 * @param ex
	 * @param request
	 * @return ResultVo
	 */
	@ExceptionHandler(Exception.class)
	@ResponseBody
	@SuppressWarnings({ "rawtypes"})
	public ResultVo handleException(Exception ex, HttpServletRequest request) {
		ResultVo resultVo = new ResultVo();
		resultVo.setErrorMsg(messageService.getMessage(ErrorCodeConstant.E_0103, getLocale(request)));
		
		resultVo.setErrorCode(ErrorCodeConstant.E_0103);
		
		NDC.push(ERROR_CODE+BaseConstant.SYMBOL_COLON+ErrorCodeConstant.E_0103);
		NDC.push(ERROR_INFO+BaseConstant.SYMBOL_COLON+resultVo.getErrorMsg());
		logger.error(ex.getMessage(),ex);
		
		return resultVo;
	}
	
	/**
	 * 客户端数据基本校验异常返回的错误信息
	 * @param ex
	 * @param request
	 * @return
	 */
	@ExceptionHandler(ConstraintViolationException.class)
	@ResponseBody
	@SuppressWarnings({ "rawtypes", "unchecked"})
	public ResultVo handleConstraintViolationException(ConstraintViolationException ex,
			HttpServletRequest request)  {
		
		List<Map<String, String>> list = new ArrayList<Map<String, String>>(); 
		Locale locale = getLocale(request);
		
		//组装所有错误字段和错误描述
		for (ConstraintViolation<?> constraintViolation : ex.getConstraintViolations()) {
			Map<String, String> map = new HashMap<String, String>();
			String propertyPath = constraintViolation.getPropertyPath().toString();
			String message = constraintViolation.getMessage();
			map.put(FIELD, propertyPath);
			map.put(ERROR_INFO, messageService.getMessage(message, locale));
			// map.put(ERROR_CODE, StringUtil.getError(ex.getMessage(), message));
			map.put(ERROR_CODE, message);
			list.add(map);
		}
		
		ResultVo resultVo = new ResultVo();
		resultVo.setData(list);
		
		resultVo.setErrorCode(ErrorCodeConstant.E_0003);
		resultVo.setErrorMsg(messageService.getMessage(ErrorCodeConstant.E_0003, locale));

		NDC.push(ERROR_CODE+BaseConstant.SYMBOL_COLON+ErrorCodeConstant.E_0003);
		NDC.push(ERROR_INFO+BaseConstant.SYMBOL_COLON+resultVo.getErrorMsg());
		NDC.push(DATA+BaseConstant.SYMBOL_COLON+JsonBinder.buildNonDefaultBinder().toJson(list));
		logger.error(ex.getMessage(),ex);
		
		return resultVo;
	}

	public MessageService getMessageService() {
		return messageService;
	}
	
	private Locale getLocale(HttpServletRequest request){
		RequestContext requestContext = new RequestContext(request);
		return requestContext.getLocale();
	}
	
	
}
